# -*- coding: utf-8 -*-
 ###############################################
 ##                                             ##
 ##       Data model for Products package       ##
 ##                                             ##
 ##                                             ##
 ##              from Basiq Series              ##
 ##           by Críptidos Digitales            ##
 ##                 GPL (c)2008                 ##
  ##                                            ##
    ##############################################

"""
"""

from decimal import Decimal

from baseModel import *



def agrega(datos):
    print "productos.model.agrega()"
    print datos

    currentDateTime = datetime.datetime.today()

    ## CLASIFICACIÓN
    if not 'clasificacion_id' in datos.keys():
        if 'clasificacion' in datos.keys():
            clasificacion = model.getAttribute(grupo=u'clasificacionProducto', nombre=datos['clasificacion'])
            # try:
                # clasificacion = session().query(Atributo).filter(Atributo.grupo==u'clasificacionProducto').filter(Atributo.nombre==datos['clasificacion']).one()
            # except:
                # clasificacion = Atributo(grupo=u'clasificacionProducto', nombre=datos['clasificacion'])
                # man.session().add(clasificacion)
                # man.session().flush()
            datos['clasificacion_id'] = clasificacion['id']

    ## PRODUCTO
    product = set('productos', clasificacion_id = datos['clasificacion_id'], tipo_id = datos['tipo_id'], unidad_id = datos['unidad_id'], minimo = datos['minimo'], maximo = datos['maximo'], actual = datos['actual'], impuesto = datos['impuesto'], margen = datos['margen'], costopromedio = datos['costopromedio'],status = 'activo', fechaalta = currentDateTime)

    ## ACEPCION LOCAL
    aception = set('acepciones', producto_id = product['id'], rol_id = session.app.holder['id'], codigo = datos['codigo'], nombre = datos['nombre'], costo = datos['costopromedio'], descuentoindividual = Decimal("0"), descuentogeneral = Decimal("0"), status = u'actual', fechaalta = currentDateTime)

    product['aceptions'] = [aception]
    ## Acepciones externas
    # aceptions = []
    for index, aception in enumerate(datos['aceptions']):
        # data = datos['acepciones'][index]

        aception['producto_id'] = product['id']
        aception['fechaalta'] = currentDateTime
        aception = set('acepciones', **aception)
        product['aceptions'].append(aception)

    product['lines'] = []
    ## LINEA
    for indexLinea, linea in enumerate(datos['lineas']):
        if 'atributo_id' not in linea.keys():
            linea['atributo_id'] = setAttribute(grupo=u'líneaProducto', nombre=linea.pop('atributo_nombre'))['id']

        productAttribute = set('atributosproducto', producto_id=product['id'], nombre=u'línea', atributo_id=linea['atributo_id'], status=u'actual', fechaalta=currentDateTime)
        product['lines'].append(productAttribute)

    product['prices'] = []
    ## PRECIOS
    for index, price in enumerate(datos['prices']):
        price['valor'] = '%s' % price['valor']
        price['factor1'] = '%s' % price['factor1']
        price['factor2'] = '%s' % price['factor2']
        price['producto_id'] = product['id']
        price['nombre'] = u'precio'
        price['status'] = u'actual'
        price['fechaalta'] = currentDateTime
        productAttribute = set('atributosproducto', **price)
        product['prices'].append(productAttribute)

    return product


def capturaLineaHabilitada():
    return not not getAttribute(grupo=u'producto', nombre=u'capturaLineaHabilitada')['valor']

def getClasifications():
    clasifications = getAttributes(grupo=u'clasificacionProducto', order=u'nombre')
    return clasifications

def getLines():
    lines = getAttributes(grupo=u'líneaProducto', order=u'nombre')
    return lines



def aceptionExists(**filtros):
    # print "productos.model.aceptionExists()"
    filtros['status'] = u'actual'
    aception = getOne('acepciones', **filtros)
    return aception

    #~ if 'codigo' in filtros.keys():
        #~ registro = man.session().query(Acepcion).filter(Acepcion.codigo==filtros['codigo']).filter(Acepcion.rol_id==filtros['rol_id']).filter(Acepcion.status==u'actual').one()
    #~ else:
        #~ registro = man.session().query(Acepcion).filter(Acepcion.producto_id==filtros['producto_id']).filter(Acepcion.rol_id==filtros['rol_id']).filter(Acepcion.status==u'actual').first()
        #~ if registro:
            #~ return registro
        #~ else:
            #~ return None
    #~ return registro




def activePriceRules():
    data = getAttributes(grupo='rangoPrecioProducto', order='referencia')
    active = []
    for rule in data:
        if rule['referencia'][1] == 'a':
            active.append(rule)
    return active


def capturaClasificacionObligatoria():
    return not not getAttribute(grupo=u'producto', nombre=u'capturaClasificacionObligatoria')['valor']


def clasificacion(**filters):
    clasificacion = getAttribute(**filters)
    return clasificacion


def currentStockEditable():
    if getAttribute(grupo=u'producto', nombre=u'capturaActualPermitida')['valor'] == u"1":
        return True
    else:
        return False


def defaultProductType():
    productType = getAttribute(grupo=u'tipoProducto', valor=u'default')
    return productType


def defaultUnit():
    unit = getAttribute(grupo=u'unidad', valor=u'default')
    return unit


def documentItems(**filters):
    command = """SELECT partidasdocumento.*, documentos.fecha AS document_date, documentos.folio AS document_serial,
documentos.descuentoporcentaje AS document_discountpercent,
entidades.nombre AS rol_name, entidades.nombre2 AS rol_name2,
operaciones.operaciones_tipo

FROM partidasdocumento
JOIN documentos USING (documento_id)
JOIN roles ON (documentos.rol_id=roles.id)
JOIN entidades ON (roles.entidad_id=entidades.id)
JOIN operaciones_documentos USING (documento_id)
JOIN

(SELECT operacion_id, array_agg(ops.operacion_tipo) AS operaciones_tipo
	FROM operaciones as ops
	WHERE ops.operacion_tipo IN ('entrada', 'salida')
	GROUP BY operacion_id
	) AS operaciones

USING (operacion_id)
 """

    filtersText = ''
    for filter in filters.keys():
        if filter == 'tipo':
            if type(filters[filter]) == list:
                command += "JOIN atributos ON (documentos.tipo_id=atributos.id) "
                print type("atributos.nombre IN %s AND " % (tuple(filters[filter]),))
                filtersText += "atributos.nombre IN %s AND " % (tuple(filters[filter]),)
        elif type(filters[filter]) in (str, unicode, datetime.datetime):
            filtersText += "%s='%s' AND " % (filter, filters[filter])
        # elif type(filters[filter]) in (list,):
            # temp = ("%s" % filters[filter]).replace("[", "{").replace("]","}").replace("'", '"')
            # filtersText += "%s='%s' AND " % (filter, temp)
        elif filters[filter] == None:
            filtersText += "%s is null AND " % filter
        else:
            filtersText += "%s=%s AND " % (filter, filters[filter])

    command += "WHERE %s " % filtersText.rstrip("AND ")
    command += "ORDER BY documentos.fecha DESC "

    #~ print command

    query(command)
    items = fetchAll()

    return items


def elimina(**filtros):
    acepcion = man.session().query(Acepcion).filter(Acepcion.id==filtros['id']).one()

    acepciones = dameAcepciones(producto_id=acepcion.producto_id)

    for acepcion in acepciones:
        acepcion.fechabaja = datetime.datetime.today()
        acepcion.status = u'eliminada'

    producto = acepcion.producto

    producto.fechabaja = datetime.datetime.today()
    producto.status = u'eliminado'

    man.session().commit()




# def damePartidas(**filtros):
    # if 'producto_id' in filtros.keys():
        # if 'tipo' in filtros.keys():
            # registros = man.session().query(PartidaDocumento).filter(PartidaDocumento.producto_id==filtros['producto_id']).join(Documento).filter(Documento.tipo.in_(filtros['tipo'])).order_by(Documento.fecha.desc()).all()
        # else:
            # registros = man.session().query(PartidaDocumento).filter(PartidaDocumento.producto_id==filtros['producto_id']).join(Documento).order_by(Documento.fecha.desc()).all()
    # return registros





# def lines(**filters):
    # command = """SELECT atributos.* FROM atributosproducto JOIN atributos ON (atributosproducto.atributo_id=atributos.id) """
    # filtersText = """atributosproducto.nombre='línea' AND """

    # for filter in filters.keys():
        # if type(filters[filter]) in (str, unicode):
            # filtersText += "%s='%s' AND " % (filter, filters[filter])
        # elif type(filters[filter]) in (list,):
            # temp = ("%s" % filters[filter]).replace("[", "{").replace("]","}").replace("'", '"')
            # filtersText += "%s='%s' AND " % (filter, temp)
        # elif filters[filter] == None:
            # filtersText += "%s is null AND " % filter
        # else:
            # filtersText += "%s=%s AND " % (filter, filters[filter])

    # if filtersText:
        # command += "WHERE %s " % filtersText.rstrip("AND ")

    # query(command)
    # lines = fetchall()

    # return lines


def modifica(datos):
    """ La acepciones son accesadas normalmente por rol_id y producto_id, por
    lo que puede exinirse a id de su rol identificatorio y manejarse el
    archivado del registro obsoleto, despues de crearse el registro nuevo.
    El producto no puede cambiar de id por lo que el archivado se hace en el
    registro nuevo y el registro viejo actualiza su contenido
    HABRÁ QUE ANALIZAR SI LAS ACEPCIONES DEBIERAN SEGUIR ESTE COMPORTAMIENTO."""
    print "productos.manejador.modifica()"

    currentDateTime = datetime.datetime.today()

    ## CLASIFICACIÓN
    if not 'clasificacion_id' in datos.keys():
        if 'clasificacion' in datos.keys():
            try:
                clasificacion = session().query(Atributo).filter(Atributo.grupo==u'clasificacionProducto').filter(Atributo.nombre==datos['clasificacion']).one()
            except:
                clasificacion = Atributo(grupo=u'clasificacionProducto', nombre=datos['clasificacion'])
                man.session().add(clasificacion)
                man.session().flush()
            datos['clasificacion_id'] = clasificacion.id

    ## Check product register modification
    ## Extract this data from datos
    fields = ['tipo_id', 'clasificacion_id', 'unidad_id', 'upc', 'costopromedio', 'margen', 'impuesto', 'minimo', 'maximo', 'actual']
    modifiedData = {}
    [modifiedData.update({x:datos.pop(x)}) for x in [y for y in fields if y in datos.keys()]]

    print modifiedData

    if modifiedData:

        product = getProduct(id=datos['producto_id'])

        productNew = product.copy()
        productNew.pop('id')
        productNew['fechabaja'] = currentDateTime
        productNew['status'] = 'obsoleto'
        productNew = add('productos', **productNew)

        modifiedData.update({'id':product['id']})
        update('productos', **modifiedData)

    ## Check acepcion local modification
    fields = ['codigo', 'nombre', 'costo']

    modifiedData = {}
    [modifiedData.update({x:datos.pop(x)}) for x in [y for y in fields if y in datos.keys()]]

    if modifiedData:

        aceptionLocal = getAception(**{'producto_id':datos['producto_id'], 'rol_id':controller.app.holder.id, 'ac.status':u'actual'})

        aceptionNew = aceptionLocal.copy()
        aceptionNew.pop('id')
        aceptionNew['fechabaja'] = currentDateTime
        aceptionNew['status'] = 'obsoleta'
        aceptionNew = add('aceptions', **aceptionNew)

        modifiedData.update({'id':aceptionLocal['id']})
        update('aceptions', **modifiedData)


    ## Acepciones
    if 'aceptions' in datos.keys():
        for aception in datos['aceptions']:
            if 'id' in aception.keys():
                # aceptionModify(**aception)
                set('acepciones', **aception)
            else:
                aception['producto_id'] = datos['producto_id']
                aception['fechaalta'] = currentDateTime
                set('acepciones', **aception)

    ## Si hay acepciones registradas que no están en los datos recibidos, fueron eliminadas
    # roles_idOld = [x.rol_id for x in dameAcepciones('producto_id'=datos['id'])]
    # roles_idOld.pop(roles_idOld.index(-1))

    """
    acepciones = []
    acepciones2 = []

    for acepcion in datos['acepciones']:
        acepcion['producto_id'] = producto.id
        acepcionx = acepcionI(**acepcion)
    """

    # man.session().commit()

    ## Atributos
    ## LINEA
    if 'lineas' in datos.keys():
        for linea in datos['lineas']:
            if 'atributo_nombre' in linea.keys():
                linea['id'] = setAttribute(grupo=u'líneaProducto', nombre=linea.pop('atributo_nombre'))['id']
            atributo = set('atributosproducto', **linea)

    ## Precios
    if 'prices' in datos.keys():
        for price in datos['prices']:
            atributo = set('atributosproducto', **price)

    # try:
        # man.session().commit()
    # except:
        # print "puaj"
        # man.session().rollback()

    return


def prices(**filters):
    command = """SELECT atributosproducto.* FROM atributosproducto JOIN atributos ON (atributosproducto.atributo_id=atributos.id) """
    filtersText = """atributosproducto.nombre='precio' AND """

    for filter in filters.keys():
        if type(filters[filter]) in (str, unicode):
            filtersText += "%s='%s' AND " % (filter, filters[filter])
        elif type(filters[filter]) in (list,):
            temp = ("%s" % filters[filter]).replace("[", "{").replace("]","}").replace("'", '"')
            filtersText += "%s='%s' AND " % (filter, temp)
        elif filters[filter] == None:
            filtersText += "%s is null AND " % filter
        else:
            filtersText += "%s=%s AND " % (filter, filters[filter])

    if filtersText:
        command += "WHERE %s " % filtersText.rstrip("AND ")

    query(command)
    prices = fetchall()

    return prices


def productCount():
    products = getAll('productos')
    return len(products)


def productAttributes(**filters):
    productAttributes = getAll('atributosproducto', **filters)
    return productAttributes


def productLineEditable():
    return not not getAttribute(grupo=u'producto', nombre=u'capturaLineaHabilitada')['valor']


def rangosPrecio(**filtros):
    filtros['grupo'] = u'rangoPrecioProducto'
    if 'activo' in filtros.keys():
        activo = filtros.pop('activo')
        if activo:
            filtros['referenciaLIKE'] = 'a'
            #~ registros = man.session().query(Atributo).filter(Atributo.grupo==u'rangoPrecioProducto').filter(Atributo.referencia.like(u'%%a%%')).order_by(Atributo.referencia).all()
        else:
            filtros['referenciaLIKE'] = 'i'
            #~ registros = man.session().query(Atributo).filter(Atributo.grupo==u'rangoPrecioProducto').filter(Atributo.referencia.like(u'%%i%%')).order_by(Atributo.referencia).all()
    #~ else:
        #~ atributos = man.session().query(Atributo).filter(Atributo.grupo==u'rangoPrecioProducto').order_by(Atributo.referencia).all()
    attributes = getAttributes(**filtros)
    return attributes


def getSuppliers():
    supplierType = getAttribute(grupo='tipoRol', nombre='proveedor')
    query("""SELECT roles.*, e.personalidad, e.nombre, e.nombre2, e.alias, e.fechanacimiento, e.rfc, e.curp FROM roles JOIN entidades AS e ON (roles.entidad_id=e.id) WHERE tipo_id=%s ORDER BY nombre""" % supplierType['id'])
    suppliers = fetchAll()

    global __suppliersCount
    __suppliersCount = len(suppliers)

    return suppliers


@property
def suppliersCount():
    return __suppliersCount


def taxEditable():
    return not not getAttribute(grupo=u'producto', nombre=u'impuestoEnabled')['valor']


def getTipos():
    tipos = getAttributes(grupo=u'tipoProducto')
    return tipos

def getUnidades():
    unidades = getAttributes(grupo=u'unidad')
    return unidades

def usarCodigoSecundario():
    return not not getAttribute(grupo=u'producto', nombre=u'usarCódigoSecundario')['valor']

    #~ usar = model.getAttribute(grupo=u'producto', nombre=u'usarCódigoSecundario')
    #~ if usar['valor'] == '1':
        #~ return True
    #~ else:
        #~ return False

    #~ return atributoI(grupo=u'producto', nombre=u'usarCódigoSecundario').valor == '1'








#~ def productoI(**filtros):
    #~ if 'id' in filtros.keys():
        #~ try:
            #~ registro = man.session().query(Producto).filter(Producto.id==filtros['id']).one()
        #~ except:
            #~ print (2), m, '!filtros: ', filtros

    #~ return regisstro


def validate(datos, modo):
    """
        Realiza la validación de lógica de negocios
            La clasificación se recibe como una cadena para uniformizar el
            proceso de validación
    """
    valida = True
    mensajes = ""

    ## La validación de código se hace en la interfase

    ## Determinar si la clasificación ya existe, o es una nueva
    if 'clasificacion' in datos.keys():
        mensajes += u"La clasificación no existe, se creará\n"

    return valida, mensajes









################

def initDb():
    #~ print "productos.model.inicializa(",

    try:
        version = int(getAttribute(grupo=u'producto', nombre=u'versionBasedatos')['valor'])
    except:
        createDb()

    if not version:
        print "Inicializando Base de datos para productos"

        registros = [
            {'code':1605, 'grupo':u'producto', 'nombre':u'capturaActualPermitida', 'valor':u'0'},
            {'code':1607, 'grupo':u'producto', 'nombre':u'capturaLineaHabilitada', 'valor':u'1'},
            {'code':1609, 'grupo':u'producto', 'nombre':u'capturaClasificacionObligatoria', 'valor':u'1'},
            {'code':1611, 'grupo':u'producto', 'nombre':u'usarCódigoSecundario', 'valor':u'0'},
            {'code':1613, 'grupo':u'producto', 'nombre':u'useBarCode', 'valor':u'0'},

            {'code':1617, 'grupo':u'producto', 'nombre':u'descuentoEnabled', 'valor':u'1'},
            {'code':1619, 'grupo':u'producto', 'nombre':u'impuestoEnabled', 'valor':u'1'},

            {'code':1623, 'grupo':u'producto', 'nombre':u'recalcularPrecioAlComprar', 'valor':u'1'},
            {'code':1625, 'grupo':u'producto', 'nombre':u'sentidoDeCambio', 'valor':u'+/-'},
            {'code':1627, 'grupo':u'producto', 'nombre':u'margenDeCambio', 'valor':u'5'},

            {'code':1643, 'grupo':u'tipoProducto', 'nombre':u'Artículo', 'valor':u'default'},
            {'code':1645, 'grupo':u'tipoProducto', 'nombre':u'Equipo'},
            {'code':1647, 'grupo':u'tipoProducto', 'nombre':u'Parte'},
            {'code':1649, 'grupo':u'tipoProducto', 'nombre':u'Ensamble'},
            {'code':1651, 'grupo':u'tipoProducto', 'nombre':u'Servicio'},
            {'code':1653, 'grupo':u'tipoProducto', 'nombre':u'Insumo'},

            {'code':1661, 'grupo':u'rangoPrecioProducto', 'nombre':u'General', 'valor':u'default', 'referencia':u'1a'},
            {'code':1663, 'grupo':u'rangoPrecioProducto', 'nombre':u'Preferente', 'referencia':u'2i'},

            {'code':1673, 'grupo':u'unidad', 'nombre':u'-', 'referencia':u'-'},
            {'code':1675, 'grupo':u'unidad', 'nombre':u'Pieza', 'valor':u'default', 'referencia':u'pza'},
            {'code':1677, 'grupo':u'unidad', 'nombre':u'Metro', 'referencia':u'mt'},
            {'code':1679, 'grupo':u'unidad', 'nombre':u'Kilogramo', 'referencia':u'kg'}
        ]

        for registro in registros:
            setAttribute(**registro)
            #~ atributoII(**registro)

        setAttribute(grupo=u'producto', nombre=u'versionBasedatos', valor=u'11')

        version = 11


    if version < 12:

        setAttribute(code=1653, grupo=u'tipoProducto', nombre=u'Insumo')

        setAttribute(grupo=u'producto', nombre=u'versionBasedatos', valor=u'12')

        version = 12


    print ")"




def createDb():
    try:
        execute("""CREATE TABLE productos (
            id          SERIAL PRIMARY KEY,
            upc         CHARACTER VARYING,
            tipo_id     INTEGER REFERENCES atributos(id),
            clasification_id    INTEGER REFERENCES atributos(id),
            unidad_id   INTEGER REFERENCES atributos(id),
            costopromedio DECIMAL (12, 4),
            margen      DECIMAL (6, 2),
            impuesto    DECIMAL (6, 2),
            precio      DECIMAL (12, 4),
            minimo      DECIMAL (8, 2),
            maximo      DECIMAL (8, 2),
            actual      DECIMAL (8, 2),
            fechaalta   TIMESTAMP,
            fechabaja   TIMESTAMP,
            status      UNICODE
        )""")
    except:
        pass



"""
test

Un producto es una entidad única e independiente.

Puede tener algunos atributos que varían de acuerdo a la percepción de otros,
en este caso los proveedores y clientes. Ej código, nombre, clasificación,
costo.

Estos datos se registran en una tabla satelite, indicando de quien es la
percepción. De esta manera se mantiene la integridad de los datos de producto.


Determinación de códigos de productos.
    Códigos locales
    Cada vez hay más globalización en productos, lo que genera uniformidad de
    información, muchos productos tienen códigos usados internacionalmente,
    se recomienda usar estos estandares, si se requiere una diferenciación
    extra se recomienda agregar caracteres al estandar.
    Si no se cuenta con un código estándar, se recomienda usar el código del
    fabricante.

    Códigos externos
    Se usa el proporcionado por el proveedor. No se recomienda cambiarlo,
    porque se generarían errores en la comunicación con el proveedor.
    Si el proveedor cambia sus datos, EB puede registrar esos cambios sin
    problemas, sin perder información ya registrada.




Issues

Precio2 may lead to crash if capture hability is changed. Design time.

"""



